package com.atguigu.gmall.cart.service.impl;

import com.atguigu.gmall.cart.mapper.CartInfoMapper;
import com.atguigu.gmall.cart.service.CartInfoService;
import com.atguigu.gmall.common.constant.RedisConst;
import com.atguigu.gmall.common.result.Result;
import com.atguigu.gmall.model.cart.CartInfo;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductFeignClient;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.HashOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class CartInfoServiceImpl extends ServiceImpl<CartInfoMapper, CartInfo> implements CartInfoService {

    @Autowired
    private CartInfoMapper cartInfoMapper;

    @Autowired
    private ProductFeignClient productFeignClient;

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    @Transactional(readOnly = false)
    public void addToCart(Long skuId, Integer skuNum, String userId) {
        QueryWrapper<CartInfo> cartInfoQueryWrapper = new QueryWrapper<>();
        cartInfoQueryWrapper.eq("sku_id", skuId);
        cartInfoQueryWrapper.eq("user_id", userId);
        CartInfo cartInfo = cartInfoMapper.selectOne(cartInfoQueryWrapper);
        if (cartInfo != null) {
            Integer userCartSkuNum = cartInfo.getSkuNum();
            Integer userCartSkuNumNew = userCartSkuNum + skuNum;
            cartInfo.setSkuNum(userCartSkuNumNew);

            Result<BigDecimal> skuPrice = productFeignClient.getSkuPrice(skuId);
            BigDecimal priceData = skuPrice.getData();
            cartInfo.setSkuPrice(priceData);

            cartInfoMapper.updateById(cartInfo);
        }else{
            cartInfo = new CartInfo();

            Result<SkuInfo> skuInfo = productFeignClient.getSkuInfo(skuId);
            SkuInfo skuInfoData = skuInfo.getData();

            cartInfo.setUserId(userId);
            cartInfo.setSkuId(skuId);
            cartInfo.setCartPrice(skuInfoData.getPrice());

            cartInfo.setSkuNum(skuNum);

            cartInfo.setImgUrl(skuInfoData.getSkuDefaultImg());
            cartInfo.setSkuName(skuInfoData.getSkuName());
            cartInfo.setSkuPrice(skuInfoData.getPrice());

            cartInfoMapper.insert(cartInfo);
        }

        String cartKey = getCartKey(userId);
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.put(cartKey, skuId.toString(), cartInfo);
        setCartKeyExpire(cartKey);
    }

    @Override
    public List<CartInfo> getCartListFromDBToCache(String userId) {
        QueryWrapper<CartInfo> cartInfoQueryWrapper = new QueryWrapper<>();
        cartInfoQueryWrapper.eq("user_id", userId);
        List<CartInfo> cartInfos = cartInfoMapper.selectList(cartInfoQueryWrapper);
        if (!CollectionUtils.isEmpty(cartInfos)) {
            HashOperations hashOperations = redisTemplate.opsForHash();
            String cartKey = getCartKey(userId);
            for (CartInfo cartInfo : cartInfos) {
                Long skuId = cartInfo.getSkuId();
                hashOperations.put(cartKey, skuId.toString(), cartInfo);
            }
            setCartKeyExpire(cartKey);
        }
        return cartInfos;

    }

    @Override
    public List<CartInfo> getCartListFromCacheFirst(String userId) {
        if (StringUtils.isEmpty(userId)) {
            return new ArrayList<>();
        }
        String cartKey = getCartKey(userId);
        HashOperations operator = redisTemplate.opsForHash();
        List<CartInfo> cartInfoList = operator.values(cartKey);
        if (CollectionUtils.isEmpty(cartInfoList)) {
            cartInfoList = getCartListFromDBToCache(userId);
        }
        return cartInfoList;
    }

    @Override
    public List<CartInfo> getCartList(String userId, String userTempId) {
        List<CartInfo> cartInfoList = null;
        if (StringUtils.isEmpty(userId)) {
            cartInfoList= getCartListFromCacheFirst(userTempId);
            return cartInfoList;
        }
        List<CartInfo> userIdCartList = getCartListFromCacheFirst(userId);
        List<CartInfo> userTempIdList = getCartListFromCacheFirst(userTempId);

        Map<String, CartInfo> userIdCartMap = null;
        List<CartInfo> userCartList = null;
        if (!CollectionUtils.isEmpty(userTempIdList)) {
            userIdCartMap = userIdCartList.stream()
                    .collect(Collectors.toMap((cartInfo -> cartInfo.getSkuId().toString()), (cartInfo -> cartInfo)));

            for (CartInfo UserTempCartInfo : userTempIdList) {
                Long userTempSkuId = UserTempCartInfo.getSkuId();
                if (userIdCartMap.containsKey(userTempSkuId)) {
                    Integer skuNum = UserTempCartInfo.getSkuNum();
                    Integer skuNum1 = userIdCartMap.get(userTempSkuId).getSkuNum();
                    userIdCartMap.get(userTempSkuId).setSkuNum(skuNum + skuNum1);
                }else{
                    UserTempCartInfo.setUserId(userId);
                    userIdCartMap.put(userTempSkuId.toString(), UserTempCartInfo);
                }
            }

            cartInfoMapper.delete(new QueryWrapper<CartInfo>().eq("user_id", userTempId));
            cartInfoMapper.delete(new QueryWrapper<CartInfo>().eq("user_id", userId));

            userCartList = userIdCartMap.values().stream()
                    .collect(Collectors.toList());

            for (CartInfo cartInfo : userCartList) {
                cartInfoMapper.insert(cartInfo);
            }
        }else{
            userCartList = userIdCartList;
        }

        userIdCartMap = userCartList.stream().collect(Collectors.toMap((cartInfo -> cartInfo.getSkuId().toString()), (cartInfo -> cartInfo)));

        String redisKey = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.putAll(redisKey, userIdCartMap);
        redisTemplate.expire(redisKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);

        if (!CollectionUtils.isEmpty(userTempIdList)) {
            redisKey = RedisConst.USER_KEY_PREFIX + userTempId + RedisConst.USER_CART_KEY_SUFFIX;
            redisTemplate.delete(redisKey);
        }

        return userCartList;

    }

    @Override
    public void updateIsCheck(String skuId, Integer isChecked, String userId) {
        QueryWrapper<CartInfo> cartInfoQueryWrapper = new QueryWrapper<>();
        cartInfoQueryWrapper.eq("user_id", userId);
        cartInfoQueryWrapper.eq("sku_id", skuId);
        CartInfo cartInfo = cartInfoMapper.selectOne(cartInfoQueryWrapper);
        cartInfo.setIsChecked(isChecked);
        cartInfoMapper.updateById(cartInfo);

        HashOperations hashOperations = redisTemplate.opsForHash();
        String redisKey = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        hashOperations.put(redisKey, skuId, cartInfo);
        redisTemplate.expire(redisKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
        return;
    }

    @Override
    public void deleteCart(String skuId, String userId) {
        cartInfoMapper.delete(new QueryWrapper<CartInfo>().eq("user_id", userId).eq("sku_id", skuId));
        String redisKey = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(redisKey, skuId);
    }

    @Override
    public void deleteAll(String userId) {
        cartInfoMapper.delete(new QueryWrapper<CartInfo>().eq("user_id", userId).eq("is_checked",1));
        String redisKey = RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
        HashOperations hashOperations = redisTemplate.opsForHash();
        hashOperations.delete(redisKey);

        getCartListFromDBToCache(userId);
    }

    @Override
    public List<CartInfo> getCheckedCart(String userId) {
        List<CartInfo> cartInfoList = getCartListFromCacheFirst(userId);
        List<CartInfo> isCheckCartInfoList = null;
        if (!CollectionUtils.isEmpty(cartInfoList)) {
            isCheckCartInfoList = cartInfoList.stream()
                    .filter(cartInfo -> cartInfo.getIsChecked() == 1)
                    .collect(Collectors.toList());
        }
        return isCheckCartInfoList;
    }

    private String getCartKey(String userId) {
        return RedisConst.USER_KEY_PREFIX + userId + RedisConst.USER_CART_KEY_SUFFIX;
    }

    private void setCartKeyExpire(String cartKey) {
        redisTemplate.expire(cartKey, RedisConst.USER_CART_EXPIRE, TimeUnit.SECONDS);
    }
}
